package project21;

import java.util.Objects;

/**
 * Abstract base class for arithmetic problems
 */
abstract class ArithmeticProblem {
    protected int operand1;
    protected int operand2;
    protected int result;

    // Constructor
    public ArithmeticProblem(int operand1, int operand2) {
        this.operand1 = operand1;
        this.operand2 = operand2;
    }

    // Abstract method to be implemented by subclasses
    public abstract char getOperator();
    public abstract void calculateResult();

    // Common methods
    @Override
    public String toString() {
        return String.format("%2d %c %2d = %3d", operand1, getOperator(), operand2, result);
    }

    // Problem string without answer
    public String toProblemString() {
        return String.format("%2d %c %2d =   ", operand1, getOperator(), operand2);
    }

    // Override equals and hashCode for comparison
    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        ArithmeticProblem that = (ArithmeticProblem) obj;
        return operand1 == that.operand1 &&
                operand2 == that.operand2 &&
                this.getClass() == that.getClass();
    }

    @Override
    public int hashCode() {
        return Objects.hash(operand1, operand2, this.getClass());
    }

    // Get unique key for comparison
    public String getKey() {
        return operand1 + "" + getOperator() + "" + operand2;
    }

    // Getters
    public int getOperand1() {
        return operand1;
    }

    public int getOperand2() {
        return operand2;
    }

    public int getResult() {
        return result;
    }
}

/**
 * Addition problem subclass
 */
class AdditionProblem extends ArithmeticProblem {

    public AdditionProblem(int operand1, int operand2) {
        super(operand1, operand2);
        // Ensure sum doesn't exceed 100
        if (operand1 + operand2 > 100) {
            throw new IllegalArgumentException("Sum cannot exceed 100");
        }
        calculateResult();
    }

    @Override
    public char getOperator() {
        return '+';
    }

    @Override
    public void calculateResult() {
        this.result = operand1 + operand2;
    }
}

/**
 * Subtraction problem subclass
 */
class SubtractionProblem extends ArithmeticProblem {

    public SubtractionProblem(int operand1, int operand2) {
        super(operand1, operand2);
        // Ensure result is not negative
        if (operand1 < operand2) {
            throw new IllegalArgumentException("Result cannot be negative");
        }
        calculateResult();
    }

    @Override
    public char getOperator() {
        return '-';
    }

    @Override
    public void calculateResult() {
        this.result = operand1 - operand2;
    }
}

/**
 * Test class for arithmetic problem classes
 */
public class ArithmeticProblemTest {
    public static void main(String[] args) {
        System.out.println("Starting unit tests for arithmetic problem classes...\n");

        // Test counters
        int passedTests = 0;
        int totalTests = 0;

        // Test 1: Addition problem construction
        totalTests++;
        if (testAdditionConstruction()) {
            passedTests++;
            System.out.println("✓ Test 1 passed: Addition problem construction correct");
        } else {
            System.out.println("✗ Test 1 failed: Addition problem construction incorrect");
        }

        // Test 2: Subtraction problem construction
        totalTests++;
        if (testSubtractionConstruction()) {
            passedTests++;
            System.out.println("✓ Test 2 passed: Subtraction problem construction correct");
        } else {
            System.out.println("✗ Test 2 failed: Subtraction problem construction incorrect");
        }

        // Test 3: Addition calculation
        totalTests++;
        if (testAdditionCalculation()) {
            passedTests++;
            System.out.println("✓ Test 3 passed: Addition calculation correct");
        } else {
            System.out.println("✗ Test 3 failed: Addition calculation incorrect");
        }

        // Test 4: Subtraction calculation
        totalTests++;
        if (testSubtractionCalculation()) {
            passedTests++;
            System.out.println("✓ Test 4 passed: Subtraction calculation correct");
        } else {
            System.out.println("✗ Test 4 failed: Subtraction calculation incorrect");
        }

        // Test 5: Addition boundary values
        totalTests++;
        if (testAdditionBoundaries()) {
            passedTests++;
            System.out.println("✓ Test 5 passed: Addition boundaries correct");
        } else {
            System.out.println("✗ Test 5 failed: Addition boundaries incorrect");
        }

        // Test 6: Subtraction boundary values
        totalTests++;
        if (testSubtractionBoundaries()) {
            passedTests++;
            System.out.println("✓ Test 6 passed: Subtraction boundaries correct");
        } else {
            System.out.println("✗ Test 6 failed: Subtraction boundaries incorrect");
        }

        // Test 7: toString method
        totalTests++;
        if (testToString()) {
            passedTests++;
            System.out.println("✓ Test 7 passed: toString method correct");
        } else {
            System.out.println("✗ Test 7 failed: toString method incorrect");
        }

        // Test 8: toProblemString method
        totalTests++;
        if (testToProblemString()) {
            passedTests++;
            System.out.println("✓ Test 8 passed: toProblemString method correct");
        } else {
            System.out.println("✗ Test 8 failed: toProblemString method incorrect");
        }

        // Test 9: equals and hashCode methods
        totalTests++;
        if (testEqualsAndHashCode()) {
            passedTests++;
            System.out.println("✓ Test 9 passed: equals and hashCode methods correct");
        } else {
            System.out.println("✗ Test 9 failed: equals and hashCode methods incorrect");
        }

        // Test 10: getKey method
        totalTests++;
        if (testGetKey()) {
            passedTests++;
            System.out.println("✓ Test 10 passed: getKey method correct");
        } else {
            System.out.println("✗ Test 10 failed: getKey method incorrect");
        }

        // Print test results summary
        System.out.println("\n==================== Test Results ====================");
        System.out.println("Total tests: " + totalTests);
        System.out.println("Passed tests: " + passedTests);
        System.out.println("Failed tests: " + (totalTests - passedTests));
        System.out.println("Pass rate: " + String.format("%.2f", (passedTests * 100.0 / totalTests)) + "%");

        if (passedTests == totalTests) {
            System.out.println("🎉 All tests passed!");
        } else {
            System.out.println("❌ Some tests failed, please check the code.");
        }
    }

    // Test addition problem construction
    private static boolean testAdditionConstruction() {
        try {
            AdditionProblem problem = new AdditionProblem(10, 20);
            return problem.getOperand1() == 10 &&
                    problem.getOperand2() == 20 &&
                    problem.getOperator() == '+' &&
                    problem.getResult() == 30;
        } catch (Exception e) {
            return false;
        }
    }

    // Test subtraction problem construction
    private static boolean testSubtractionConstruction() {
        try {
            SubtractionProblem problem = new SubtractionProblem(30, 15);
            return problem.getOperand1() == 30 &&
                    problem.getOperand2() == 15 &&
                    problem.getOperator() == '-' &&
                    problem.getResult() == 15;
        } catch (Exception e) {
            return false;
        }
    }

    // Test addition calculation
    private static boolean testAdditionCalculation() {
        try {
            AdditionProblem problem = new AdditionProblem(25, 35);
            return problem.getResult() == 60;
        } catch (Exception e) {
            return false;
        }
    }

    // Test subtraction calculation
    private static boolean testSubtractionCalculation() {
        try {
            SubtractionProblem problem = new SubtractionProblem(50, 20);
            return problem.getResult() == 30;
        } catch (Exception e) {
            return false;
        }
    }

    // Test addition boundaries
    private static boolean testAdditionBoundaries() {
        try {
            // Test maximum values
            AdditionProblem problem1 = new AdditionProblem(100, 0);
            if (problem1.getResult() != 100) return false;

            AdditionProblem problem2 = new AdditionProblem(50, 50);
            if (problem2.getResult() != 100) return false;

            return true;
        } catch (Exception e) {
            return false;
        }
    }

    // Test subtraction boundaries
    private static boolean testSubtractionBoundaries() {
        try {
            // Test boundary values
            SubtractionProblem problem1 = new SubtractionProblem(100, 100);
            if (problem1.getResult() != 0) return false;

            SubtractionProblem problem2 = new SubtractionProblem(50, 0);
            if (problem2.getResult() != 50) return false;

            return true;
        } catch (Exception e) {
            return false;
        }
    }

    // Test toString method
    private static boolean testToString() {
        try {
            AdditionProblem addProblem = new AdditionProblem(5, 8);
            String addResult = addProblem.toString();

            SubtractionProblem subProblem = new SubtractionProblem(12, 7);
            String subResult = subProblem.toString();

            return addResult.contains("5 +  8 =  13") && subResult.contains("12 -  7 =   5");
        } catch (Exception e) {
            return false;
        }
    }

    // Test toProblemString method
    private static boolean testToProblemString() {
        try {
            AdditionProblem addProblem = new AdditionProblem(15, 25);
            String addResult = addProblem.toProblemString();

            SubtractionProblem subProblem = new SubtractionProblem(40, 15);
            String subResult = subProblem.toProblemString();

            return addResult.contains("15 + 25 =") && subResult.contains("40 - 15 =");
        } catch (Exception e) {
            return false;
        }
    }

    // Test equals and hashCode methods
    private static boolean testEqualsAndHashCode() {
        try {
            AdditionProblem problem1 = new AdditionProblem(10, 5);
            AdditionProblem problem2 = new AdditionProblem(10, 5);
            AdditionProblem problem3 = new AdditionProblem(10, 6);

            // Same problems should be equal
            boolean equalCheck = problem1.equals(problem2);

            // Same problems should have same hashCode
            boolean hashCodeCheck = problem1.hashCode() == problem2.hashCode();

            // Different problems should not be equal
            boolean notEqualCheck = !problem1.equals(problem3);

            return equalCheck && hashCodeCheck && notEqualCheck;
        } catch (Exception e) {
            return false;
        }
    }

    // Test getKey method
    private static boolean testGetKey() {
        try {
            AdditionProblem addProblem = new AdditionProblem(25, 10);
            String addKey = addProblem.getKey();

            SubtractionProblem subProblem = new SubtractionProblem(30, 15);
            String subKey = subProblem.getKey();

            return addKey.equals("25+10") && subKey.equals("30-15");
        } catch (Exception e) {
            return false;
        }
    }
}
